package com.tax.infrastructure.statemachine.impl;

import com.tax.infrastructure.statemachine.State;
import com.tax.infrastructure.statemachine.StateMachine;
import com.tax.infrastructure.statemachine.Transition;
import com.tax.infrastructure.statemachine.Visitor;

import java.util.Map;
import java.util.Optional;

/**
 * for performance consideration
 * the state machine is made "stateless" on purpose
 * once it's built ,it can be shared by multi-thread
 *
 * one side effect is since the state machine is stateless,we can not get current state from state machine
 * @param <S>
 * @param <E>
 * @param <C>
 */
public class StateMachineImpl<S, E, C> implements StateMachine<S, E, C> {

    private String machineId;

    private final Map<S, State<S,E,C>> stateMap;

    private boolean ready;

    public StateMachineImpl(Map<S, State<S, E, C>> stateMap) {
        this.stateMap = stateMap;
    }


    @Override
    public S fireEvent(S sourceStateId, E event, C ctx) {
        isReady();
        State sourceState = getState(sourceStateId);
        return doTransition(sourceState,event,ctx).getId();
    }

    private State<S,E,C> doTransition(State sourceState, E event, C ctx) {
        Optional<Transition<S, E, C>> transition = sourceState.getTransition(event);
        if (transition.isPresent()){
            return transition.get().transit(ctx);
        }
        Debugger.debug("There is no Transition for "+event);
        return sourceState;
    }

    private State getState(S currentStateId) {
        State<S, E, C> state = StateHelper.getState(stateMap, currentStateId);
        if (null==state){
            showStateMachine();
            throw new StateMachineException(currentStateId+" is not found,please check state machine");
        }
        return state;
    }

    @Override
    public String getMachineId() {

        return machineId;
    }

    private void isReady() {
        if (!ready){
            throw new StateMachineException("State machine is not built yet,can not work");
        }
    }

    @Override
    public void showStateMachine() {

    }

    @Override
    public String generatePlantUML() {
        return null;
    }

    @Override
    public String accept(Visitor visitor) {
        return null;
    }

    public void setMachineId(String machineId) {
        this.machineId=machineId;
    }

    public void setReady(boolean ready) {
        this.ready=ready;
    }
}
